/***************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名	: 	 bsp_lcd.c
作者	   : 左忠凯
版本	   : V1.0
描述	   : LCD驱动文件。
其他	   : 无
论坛 	   : www.wtmembed.com
日志	   : 初版V1.0 2019/1/3 左忠凯创建
***************************************************************/
#include "bsp_lcd.h"
#include "bsp_gpio.h"
#include "bsp_delay.h"
#include "stdio.h"
inline void lcd_next_buff(void);

/* 液晶屏参数结构体 */
struct tftlcd_typedef tftlcd_dev;

static void lcd_io_init()
{
	struct io_conf
	{
		uint32_t muxRegister;
		uint32_t muxMode;
		uint32_t inputRegister;
		uint32_t inputDaisy;
		uint32_t configRegister;
	};
	IOMUXC_PAD_t config = {
		.SPEED = IOMUXC_PAD_SPEED_MID,
		.DSE = IOMUXC_PAD_DSE_R0_7};

	struct io_conf lcdconf[] = {
		{IOMUXC_LCD_DATA00_LCDIF_DATA00},
		{IOMUXC_LCD_DATA01_LCDIF_DATA01},
		{IOMUXC_LCD_DATA02_LCDIF_DATA02},
		{IOMUXC_LCD_DATA03_LCDIF_DATA03},
		{IOMUXC_LCD_DATA04_LCDIF_DATA04},
		{IOMUXC_LCD_DATA05_LCDIF_DATA05},
		{IOMUXC_LCD_DATA06_LCDIF_DATA06},
		{IOMUXC_LCD_DATA07_LCDIF_DATA07},
		{IOMUXC_LCD_DATA08_LCDIF_DATA08},
		{IOMUXC_LCD_DATA09_LCDIF_DATA09},
		{IOMUXC_LCD_DATA10_LCDIF_DATA10},
		{IOMUXC_LCD_DATA11_LCDIF_DATA11},
		{IOMUXC_LCD_DATA12_LCDIF_DATA12},
		{IOMUXC_LCD_DATA13_LCDIF_DATA13},
		{IOMUXC_LCD_DATA14_LCDIF_DATA14},
		{IOMUXC_LCD_DATA15_LCDIF_DATA15},
		{IOMUXC_LCD_DATA16_LCDIF_DATA16},
		{IOMUXC_LCD_DATA17_LCDIF_DATA17},
		{IOMUXC_LCD_DATA18_LCDIF_DATA18},
		{IOMUXC_LCD_DATA19_LCDIF_DATA19},
		{IOMUXC_LCD_DATA20_LCDIF_DATA20},
		{IOMUXC_LCD_DATA21_LCDIF_DATA21},
		{IOMUXC_LCD_DATA22_LCDIF_DATA22},
		{IOMUXC_LCD_DATA23_LCDIF_DATA23},

		{IOMUXC_LCD_CLK_LCDIF_CLK},
		{IOMUXC_LCD_ENABLE_LCDIF_ENABLE},
		{IOMUXC_LCD_HSYNC_LCDIF_HSYNC},
		{IOMUXC_LCD_VSYNC_LCDIF_VSYNC}};
	int i = 0;
	for (i = 0; i < sizeof(lcdconf) / sizeof(*lcdconf); i++)
	{
		io_pin_config(lcdconf[i].muxRegister, lcdconf[i].muxMode, lcdconf[i].inputRegister,
					  lcdconf[i].inputDaisy, lcdconf[i].configRegister,
					  0, config.full);
	}
	// 背光
	io_pin_config(IOMUXC_GPIO1_IO08_GPIO1_IO08, 0, config.full);
	gpio_init_output(GPIO1, 8, 0);
}
void lcd_bl_on()
{
	gpio_pinwrite(GPIO1, 8, 1);
}
void lcd_bl_off()
{

	gpio_pinwrite(GPIO1, 8, 0);
}

/*
 * @description	: 始化LCD
 * @param 		: 无
 * @return 		: 无
 */
void lcd_init(void)
{
	unsigned short lcdid = 0;

	lcdid = lcd_read_panelid(); /* 读取屏幕ID值 		*/
	printf("LCD ID=%#X\r\n", lcdid);
	lcd_io_init();
	lcd_reset();   /* 复位LCD  			*/
	delayms(10);   /* 延时10ms 			*/
	lcd_noreset(); /* 结束复位 			*/

	/* TFTLCD参数结构体初始化 */
	if (lcdid == ATK4342)
	{
		tftlcd_dev.height = 272;
		tftlcd_dev.width = 480;
		tftlcd_dev.vspw = 1;
		tftlcd_dev.vbpd = 8;
		tftlcd_dev.vfpd = 8;
		tftlcd_dev.hspw = 1;
		tftlcd_dev.hbpd = 40;
		tftlcd_dev.hfpd = 5;
		lcdclk_init(27, 8, 8); /* 初始化LCD时钟 10.1MHz */
	}
	else if (lcdid == ATK4384)
	{
		tftlcd_dev.height = 480;
		tftlcd_dev.width = 800;
		tftlcd_dev.vspw = 3;
		tftlcd_dev.vbpd = 32;
		tftlcd_dev.vfpd = 13;
		tftlcd_dev.hspw = 48;
		tftlcd_dev.hbpd = 88;
		tftlcd_dev.hfpd = 40;
		lcdclk_init(42, 4, 8); /* 初始化LCD时钟 31.5MHz */
	}
	else if (lcdid == ATK7084)
	{
		tftlcd_dev.height = 480;
		tftlcd_dev.width = 800;
		tftlcd_dev.vspw = 1;
		tftlcd_dev.vbpd = 23;
		tftlcd_dev.vfpd = 22;
		tftlcd_dev.hspw = 1;
		tftlcd_dev.hbpd = 46;
		tftlcd_dev.hfpd = 210;
		lcdclk_init(30, 3, 7); /* 初始化LCD时钟 34.2MHz */
	}
	else if (lcdid == ATK7016)
	{
		tftlcd_dev.height = 600;
		tftlcd_dev.width = 1024;
		tftlcd_dev.vspw = 3;
		tftlcd_dev.vbpd = 20;
		tftlcd_dev.vfpd = 12;
		tftlcd_dev.hspw = 20;
		tftlcd_dev.hbpd = 140;
		tftlcd_dev.hfpd = 160;
		lcdclk_init(32, 3, 5); /* 初始化LCD时钟 51.2MHz */
	}
	tftlcd_dev.pixsize = 4; /* ARGB8888模式，每个像素4字节 */
	tftlcd_dev.framebuffer1 = LCD_FRAMEBUF_ADDR;
	tftlcd_dev.framebuffer2 = LCD_FRAMEBUF_ADDR2;
	tftlcd_dev.backcolor = LCD_WHITE; /* 背景色为白色 */
	tftlcd_dev.forecolor = LCD_BLACK; /* 前景色为黑色 */

	/* 初始化ELCDIF的CTRL寄存器
     * bit [31] 0 : 停止复位
     * bit [19] 1 : 旁路计数器模式
     * bit [17] 1 : LCD工作在dotclk模式
     * bit [15:14] 00 : 输入数据不交换
     * bit [13:12] 00 : CSC不交换
     * bit [11:10] 11 : 24位总线宽度
     * bit [9:8]   11 : 24位数据宽度,也就是RGB888
     * bit [5]     1  : elcdif工作在主模式
     * bit [1]     0  : 所有的24位均有效
	 */
	LCDIF_CTRL_t *lcd_ctrl = (LCDIF_CTRL_t *)&LCDIF->CTRL;
	/*
	 LCDIF->CTRL |= (1 << 19) | (1 << 17) | (0 << 14) | (0 << 12) |
	 				(3 << 10) | (3 << 8) | (1 << 5) | (0 << 1);
           */
	lcd_ctrl->BYPASS_COUNT = 1; // 必须是1
	lcd_ctrl->DOTCLK_MODE = 1;
	lcd_ctrl->INPUT_DATA_SWIZZL_E = 0; // 不交换
	lcd_ctrl->CSC_DATA_SWIZZL_E = 0;   // 不交换
	lcd_ctrl->LCD_DATABUS_WIDTH = 3;   // 24bit 3个字节
	lcd_ctrl->WORD_LENGTH = 3;		   // 24bit
	lcd_ctrl->MASTER = 1;			   // 主机模式
									   /*
     * 初始化ELCDIF的寄存器CTRL1
     * bit [19:16]  : 0X7 ARGB模式下，传输24位数据，A通道不用传输
	 */
									   // LCDIF->CTRL1 = 0X7 << 16;
	REG_BITVAL(LCDIF, CTRL1)->BYTE_PACKING_FORMAT = 7;

	/*
      * 初始化ELCDIF的寄存器TRANSFER_COUNT寄存器
      * bit [31:16]  : 高度
      * bit [15:0]   : 宽度
	  */
	REG_BITVAL(LCDIF, TRANSFER_COUNT)->V_COUNT = tftlcd_dev.height;
	REG_BITVAL(LCDIF, TRANSFER_COUNT)->H_COUNT = tftlcd_dev.width;
	/*
     * 初始化ELCDIF的VDCTRL0寄存器
     * bit [29] 0 : VSYNC输出
     * bit [28] 1 : 使能ENABLE输出
     * bit [27] 0 : VSYNC低电平有效
     * bit [26] 0 : HSYNC低电平有效
     * bit [25] 0 : DOTCLK上升沿有效
     * bit [24] 1 : ENABLE信号高电平有效
     * bit [21] 1 : DOTCLK模式下设置为1
     * bit [20] 1 : DOTCLK模式下设置为1
     * bit [17:0] : vsw参数
	 */
	REG_BITVAL(LCDIF, VDCTRL0)->full = 0;
	REG_BITVAL(LCDIF, VDCTRL0)->VSYNC_OEB = 0;
	REG_BITVAL(LCDIF, VDCTRL0)->ENABLEPRESENT = 1;
	REG_BITVAL(LCDIF, VDCTRL0)->VSYNC_POL = 0;
	REG_BITVAL(LCDIF, VDCTRL0)->HSYNC_POL = 0;
	REG_BITVAL(LCDIF, VDCTRL0)->DOTCLK_POL = 0;
	REG_BITVAL(LCDIF, VDCTRL0)->ENABLE_POL = 1;
	REG_BITVAL(LCDIF, VDCTRL0)->VSYNC_PERIOD_UNIT = 1;
	REG_BITVAL(LCDIF, VDCTRL0)->VSYNC_PULSE_WIDTH = tftlcd_dev.vspw;
	/*
	 * 初始化ELCDIF的VDCTRL1寄存器
	 * 设置VSYNC总周期
	 */
	LCDIF->VDCTRL1 = tftlcd_dev.height + tftlcd_dev.vspw + tftlcd_dev.vfpd + tftlcd_dev.vbpd; //VSYNC周期
	/*
	  * 初始化ELCDIF的VDCTRL2寄存器
	  * 设置HSYNC周期
	  * bit[31:18] ：hsw
	  * bit[17:0]  : HSYNC总周期
	  */
	REG_BITVAL(LCDIF, VDCTRL2)->HSYNC_PULSE_WIDTH = tftlcd_dev.hspw;
	REG_BITVAL(LCDIF, VDCTRL2)->HSYNC_PERIOD = (tftlcd_dev.width + tftlcd_dev.hspw + tftlcd_dev.hfpd + tftlcd_dev.hbpd);
	/*
	 * 初始化ELCDIF的VDCTRL3寄存器
	 * 设置HSYNC周期
	 * bit[27:16] ：水平等待时钟数
	 * bit[15:0]  : 垂直等待时钟数
	 */
	REG_BITVAL(LCDIF, VDCTRL3)->HORIZONTAL_WAIT_CNT = tftlcd_dev.hbpd + tftlcd_dev.hspw;
	REG_BITVAL(LCDIF, VDCTRL3)->VERTICAL_WAIT_CNT = tftlcd_dev.vbpd + tftlcd_dev.vspw;
	/*
	 * 初始化ELCDIF的VDCTRL4寄存器
	 * 设置HSYNC周期
	 * bit[18] 1 : 当使用VSHYNC、HSYNC、DOTCLK的话此为置1
	 * bit[17:0]  : 宽度
	 */

	REG_BITVAL(LCDIF, VDCTRL4)->SYNC_SIGNALS_ON = 1;
	REG_BITVAL(LCDIF, VDCTRL4)->DOTCLK_H_VALID_DATA_CNT = tftlcd_dev.width;


	/*
	* 显示地址先不着争设置，等填充好数据后，调用 lcd_next_buff(),后更新
	 */
	lcd_enable(); /* 使能LCD 	*/
	lcd_clear(LCD_WHITE); /* 清屏 		*/
	lcd_next_buff();
	delayms(100);
	lcd_bl_on();
}
static int buff_index = 0;
inline void lcd_next_buff()
{
	buff_index = !buff_index;
	if (LCDIF->CUR_BUF == tftlcd_dev.framebuffer1)
	{
		LCDIF->NEXT_BUF = tftlcd_dev.framebuffer2;
	}
	else
	{
		LCDIF->NEXT_BUF = tftlcd_dev.framebuffer1;
	}
}
// 返回当绘制缓冲区
inline uint32_t lcd_buff()
{

	if (LCDIF->CUR_BUF == tftlcd_dev.framebuffer1)
	{
		return tftlcd_dev.framebuffer2;
	}
	return tftlcd_dev.framebuffer1;
}
/*
 * 读取屏幕ID，
 * 描述：LCD_DATA23=R7(M0);LCD_DATA15=G7(M1);LCD_DATA07=B7(M2);
 * 		M2:M1:M0
 *		0 :0 :0	//4.3寸480*272 RGB屏,ID=0X4342
 *		0 :0 :1	//7寸800*480 RGB屏,ID=0X7084
 *	 	0 :1 :0	//7寸1024*600 RGB屏,ID=0X7016
 *  	1 :0 :1	//10.1寸1280*800,RGB屏,ID=0X1018
 *		1 :0 :0	//4.3寸800*480 RGB屏,ID=0X4384
 * @param 		: 无
 * @return 		: 屏幕ID
 */
unsigned short lcd_read_panelid(void)
{
	unsigned char idx = 0;

	/* 配置屏幕ID信号线 */
	IOMUXC_SetPinMux(IOMUXC_LCD_VSYNC_GPIO3_IO03, 0);
	IOMUXC_SetPinConfig(IOMUXC_LCD_VSYNC_GPIO3_IO03, 0X10B0);

	/* 打开模拟开关 */
	gpio_pin_config_t idio_config;
	idio_config.direction = kGPIO_DigitalOutput;
	idio_config.outputLogic = 1;
	gpio_init(GPIO3, 3, &idio_config);

	/* 读取ID值，设置G7 B7 R7为输入 */
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA07_GPIO3_IO12, 0); /* B7(M2) */
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA15_GPIO3_IO20, 0); /* G7(M1) */
	IOMUXC_SetPinMux(IOMUXC_LCD_DATA23_GPIO3_IO28, 0); /* R7(M0) */

	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA07_GPIO3_IO12, 0xF080);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA15_GPIO3_IO20, 0xF080);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA23_GPIO3_IO28, 0xF080);

	idio_config.direction = kGPIO_DigitalInput;
	gpio_init(GPIO3, 12, &idio_config);
	gpio_init(GPIO3, 20, &idio_config);
	gpio_init(GPIO3, 28, &idio_config);

	idx = (unsigned char)gpio_pinread(GPIO3, 28);		/* 读取M0 */
	idx |= (unsigned char)gpio_pinread(GPIO3, 20) << 1; /* 读取M1 */
	idx |= (unsigned char)gpio_pinread(GPIO3, 12) << 2; /* 读取M2 */

	if (idx == 0)
		return ATK4342; //4.3寸屏,480*272分辨率
	else if (idx == 1)
		return ATK7084; //7寸屏,800*480分辨率
	else if (idx == 2)
		return ATK7016; //7寸屏,1024*600分辨率
	else if (idx == 4)
		return ATK4384; //4寸屏,800*480分辨率
	else if (idx == 5)
		return ATK1018; //10.1寸屏,1280*800分辨率
	else
		return 0;
}

/*
 * @description		: LCD时钟初始化, LCD时钟计算公式如下：
 *                	  LCD CLK = 24 * loopDiv / prediv / div
 * @param -	loopDiv	: loopDivider值
 * @param -	prediv  : lcdifprediv值
 * @param -	div		: lcdifdiv值
 * @return 			: 无
 */
void lcdclk_init(unsigned char loopDiv, unsigned char prediv, unsigned char div)
{
	/* 先初始化video pll 
     * VIDEO PLL = OSC24M * (loopDivider + (denominator / numerator)) / postDivider
 	 *不使用小数分频器，因此denominator和numerator设置为0
 	 */
	CCM_ANALOG->PLL_VIDEO_NUM = 0; /* 不使用小数分频器 */
	CCM_ANALOG->PLL_VIDEO_DENOM = 0;

	/*
     * PLL_VIDEO寄存器设置
     * bit[13]:    1   使能VIDEO PLL时钟
     * bit[20:19]  2  设置postDivider为1分频
     * bit[6:0] : 32  设置loopDivider寄存器
	 */
	CCM_ANALOG->PLL_VIDEO = (2 << 19) | (1 << 13) | (loopDiv << 0);

	/*
     * MISC2寄存器设置
     * bit[31:30]: 0  VIDEO的post-div设置，时钟源来源于postDivider，1分频
	 */
	CCM_ANALOG->MISC2 &= ~(3 << 30);
	CCM_ANALOG->MISC2 = 0 << 30;

	/* LCD时钟源来源与PLL5，也就是VIDEO           PLL  */
	CCM->CSCDR2 &= ~(7 << 15);
	CCM->CSCDR2 |= (2 << 15); /* 设置LCDIF_PRE_CLK使用PLL5 */

	/* 设置LCDIF_PRE分频 */
	CCM->CSCDR2 &= ~(7 << 12);
	CCM->CSCDR2 |= (prediv - 1) << 12; /* 设置分频  */

	/* 设置LCDIF分频 */
	CCM->CBCMR &= ~(7 << 23);
	CCM->CBCMR |= (div - 1) << 23;

	/* 设置LCD时钟源为LCDIF_PRE时钟 */
	CCM->CSCDR2 &= ~(7 << 9); /* 清除原来的设置		 	*/
	CCM->CSCDR2 |= (0 << 9);  /* LCDIF_PRE时钟源选择LCDIF_PRE时钟 */
}

/*
 * @description	: 复位ELCDIF接口
 * @param 		: 无
 * @return 		: 无
 */
void lcd_reset(void)
{
	LCDIF->CTRL = 1 << 31; /* 强制复位 */
}

/*
 * @description	: 结束复位ELCDIF接口
 * @param 		: 无
 * @return 		: 无
 */
void lcd_noreset(void)
{
	LCDIF->CTRL = 0 << 31; /* 取消强制复位 */
}

/*
 * @description	: 使能ELCDIF接口
 * @param 		: 无
 * @return 		: 无
 */
void lcd_enable(void)
{
	LCDIF->CTRL |= 1 << 0; /* 使能ELCDIF */
}

/*
 * @description		: 画点函数 
 * @param - x		: x轴坐标
 * @param - y		: y轴坐标
 * @param - color	: 颜色值
 * @return 			: 无
 */
inline void lcd_drawpoint(unsigned short x, unsigned short y, unsigned int color)
{
	*(unsigned int *)((unsigned int)lcd_buff() +
					  tftlcd_dev.pixsize * (tftlcd_dev.width * y + x)) = color;
}

/*
 * @description		: 读取指定点的颜色值
 * @param - x		: x轴坐标
 * @param - y		: y轴坐标
 * @return 			: 读取到的指定点的颜色值
 */
inline unsigned int lcd_readpoint(unsigned short x, unsigned short y)
{
	return *(unsigned int *)((unsigned int)lcd_buff() +
							 tftlcd_dev.pixsize * (tftlcd_dev.width * y + x));
}

/*
 * @description		: 清屏
 * @param - color	: 颜色值
 */
void lcd_clear(unsigned int color)
{
	unsigned int num;
	unsigned int i = 0;

	unsigned int *startaddr = (unsigned int *)lcd_buff();	  //指向帧缓存首地址
	num = (unsigned int)tftlcd_dev.width * tftlcd_dev.height; //缓冲区总长度
	for (i = 0; i < num; i++)
	{
		startaddr[i] = color;
	}
}

/*
 * @description		: 以指定的颜色填充一块矩形
 * @param - x0		: 矩形起始点坐标X轴
 * @param - y0		: 矩形起始点坐标Y轴
 * @param - x1		: 矩形终止点坐标X轴
 * @param - y1		: 矩形终止点坐标Y轴
 * @param - color	: 要填充的颜色
 * @return 			: 读取到的指定点的颜色值
 */
void lcd_fill(unsigned short x0, unsigned short y0,
			  unsigned short x1, unsigned short y1, unsigned int color)
{
	unsigned short x, y;

	if (x0 < 0)
		x0 = 0;
	if (y0 < 0)
		y0 = 0;
	if (x1 >= tftlcd_dev.width)
		x1 = tftlcd_dev.width - 1;
	if (y1 >= tftlcd_dev.height)
		y1 = tftlcd_dev.height - 1;

	for (y = y0; y <= y1; y++)
	{
		for (x = x0; x <= x1; x++)
			lcd_drawpoint(x, y, color);
	}
}
